#pragma once

#include "mod.h"

namespace sfc::tuple {

namespace detail {

template <class... T>
struct Inn;

// clang-format off
template <>
struct Inn<>
{};

template <class T0>
struct Inn<T0>
{ T0 _0; };

template <class T0, class T1>
struct Inn<T0, T1>
{ T0 _0; T1 _1; };

template <class T0, class T1, class T2>
struct Inn<T0, T1, T2>
{ T0 _0; T1 _1; T2 _2; };

template <class T0, class T1, class T2, class T3>
struct Inn<T0, T1, T2, T3>
{ T0 _0; T1 _1; T2 _2; T3 _3; };

template <class T0, class T1, class T2, class T3, class T4>
struct Inn<T0, T1, T2, T3, T4>
{ T0 _0; T1 _1; T2 _2; T3 _3; T4 _4; };

template <class T0, class T1, class T2, class T3, class T4, class T5>
struct Inn<T0, T1, T2, T3, T4, T5>
{ T0 _0; T1 _1; T2 _2; T3 _3; T4 _4; T5 _5; };

template <class T0, class T1, class T2, class T3, class T4, class T5, class T6>
struct Inn<T0, T1, T2, T3, T4, T5, T6>
{T0 _0; T1 _1; T2 _2; T3 _3; T4 _4; T5 _5; T6 _6;};

template <class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
struct Inn<T0, T1, T2, T3, T4, T5, T6, T7>
{ T0 _0; T1 _1; T2 _2; T3 _3; T4 _4; T5 _5; T6 _6; T7 _7; };

template <class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
struct Inn<T0, T1, T2, T3, T4, T5, T6, T7, T8>
{ T0 _0; T1 _1; T2 _2; T3 _3; T4 _4; T5 _5; T6 _6; T7 _7; T8 _8; };

template <class T0, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9>
struct Inn<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>
{ T0 _0; T1 _1; T2 _2; T3 _3; T4 _4; T5 _5; T6 _6; T7 _7; T8 _8; T9 _9; };

//clang-format on

}  // namespace detail

template <class... T>
struct Tuple : detail::Inn<T...> {
  static constexpr usize SIZE = sizeof...(T);
  static_assert(SIZE <= 10);

 public:
  void map(auto&& f) const {
    if constexpr (SIZE > 0) f(this->_0);
    if constexpr (SIZE > 1) f(this->_1);
    if constexpr (SIZE > 2) f(this->_2);
    if constexpr (SIZE > 3) f(this->_3);
    if constexpr (SIZE > 4) f(this->_4);
    if constexpr (SIZE > 5) f(this->_5);
    if constexpr (SIZE > 6) f(this->_6);
    if constexpr (SIZE > 7) f(this->_7);
    if constexpr (SIZE > 8) f(this->_8);
    if constexpr (SIZE > 9) f(this->_9);
  }

  void map_mut(auto&& f) {
    if constexpr (SIZE > 0) f(this->_0);
    if constexpr (SIZE > 1) f(this->_1);
    if constexpr (SIZE > 2) f(this->_2);
    if constexpr (SIZE > 3) f(this->_3);
    if constexpr (SIZE > 4) f(this->_4);
    if constexpr (SIZE > 5) f(this->_5);
    if constexpr (SIZE > 6) f(this->_6);
    if constexpr (SIZE > 7) f(this->_7);
    if constexpr (SIZE > 8) f(this->_8);
    if constexpr (SIZE > 9) f(this->_9);
  }
};

template <class... T>
Tuple(T...) -> Tuple<T...>;

}  // namespace sfc::tuple
